using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Pipes;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.Web.UI;
using Castle.Core.Interceptor;

namespace ProcessProxy {
    public class ProcessInterceptor : IInterceptor {
        [ThreadStatic] private static LosFormatter _lf;

        private static LosFormatter lf {
            get {
                if (_lf == null)
                    _lf = new LosFormatter();
                return _lf;
            }
        }

        [ThreadStatic] private static BinaryFormatter _bf;

        private static BinaryFormatter bf {
            get {
                if (_bf == null)
                    _bf = new BinaryFormatter();
                return _bf;
            }
        }

        public string Serialize(object v) {
            using (var s = new StringWriter()) {
                lf.Serialize(s, v);
                return s.ToString();
            }
        }

        public string Quote(string o) {
            return "\"" + o + "\"";
        }

        public void Intercept(IInvocation invocation) {
            var pipename = Guid.NewGuid().ToString();
            var procArgs = new List<string> {
                Quote(invocation.TargetType.AssemblyQualifiedName),
                Quote(invocation.MethodInvocationTarget.Name),
                pipename,
            };
            procArgs.AddRange(invocation.Arguments.Select(a => Serialize(a)));
            var proc = new Process {
                StartInfo = {
                    FileName = "runner.exe",
                    Arguments = String.Join(" ", procArgs.ToArray()),
                    UseShellExecute = false,
                    CreateNoWindow = true,
                }
            };
            using (var pipe = new NamedPipeServerStream(pipename, PipeDirection.In)) {
                proc.Start();
                pipe.WaitForConnection();
                var r = bf.Deserialize(pipe);
                r = r.GetType().GetProperty("Value").GetValue(r, null);
                proc.WaitForExit();
                if (proc.ExitCode == 0) {
                    invocation.ReturnValue = r;
                } else {
                    var ex = (Exception) r;
                    throw new Exception("Error in external process", ex);
                }
            }
        }
    }
}